Documentation for Users  2.0.6
Perception Toolbox for Virtual Reality (PTVR) Manual
visual_search.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 
3 '''
4 ...\PTVR_Researchers\Python_Scripts\Experiments\visual_search.py
5 
6 
7 Important note:
8  When using a pointer, it is necessary to specify your headset's manufacturer:
9  headset_manufacturer = "Vive" (default) refers to the HTC Vive pro series headsets.
10  headset_manufacturer = "Oculus" refers to the Quest 1, 2 or 3 headsets (in keeping
11  with OpenXR terminology).
12  For example:
13 my_world = The3DWorld (
14  headset_manufacturer = "Vive" # "Vive" by default
15 
16 
17 '''
18 from PTVR.Visual import The3DWorld# Used to create the experiment
19 from PTVR.SystemUtils import LaunchThe3DWorld # Used to launch the experiment
20 from PTVR.Stimuli.Scenes import VisualScene, CheckScene # Used to create the scene
21 from PTVR.Stimuli.Objects import Cube, Sphere, AudioSource # Used to create visual object
22 from PTVR.Stimuli.Color import RGBColor
23 from PTVR.Data.Event import PointedAt, HandController
24 from PTVR.Data.Callback import AddInteractionPassedToCallback, EndCurrentScene,ChangeObjectOutline,ChangeAudioSettings
25 from PTVR.Pointing.PointingCursor import PointingLaser, LaserContingency
26 from numpy import array
27 import random
28 
29 
30 # =============================================================================
31 # PARAMETERS #
32 # =============================================================================
33 username = "Henry" # The subject's name
34 height_of_head = 1.2 # this value (in meters) is chosen so as to correspond
35 # to the height of the subject's headset during the experiment (seated or standing)
36 viewpoint_position = array ([0, height_of_head, 0])
37 # Recall: a viewpoint is the origin of the current CS used to draw
38 # the stimuli to be "seen" from this viewpoint
39 
40 
41 
42 number_of_distractor = 512
43 
44 radial_distance_min_max_values = array([2, 15])
45 
46 # Color VisualSearch
47 color_base = RGBColor(r=0, g=1, b=0) # Green
48 color_distractor = RGBColor(r=1, g=0, b=0) # Red
49 color_target = RGBColor(r=1, g=0, b=0) # Red
50 
51 
52 my_laser_color = RGBColor(r= 0.7)
53 hand = LaserContingency.RIGHT_HAND
54 
55 
56 
57 # =============================================================================
58 # END PARAMETERS #
59 # =============================================================================
60 my_world = The3DWorld(
61  headset_manufacturer = "Vive" # "Vive" by default
62  )
63 # When using a pointer, it is necessary to specify your headset's manufacturer:
64 # headset_manufacturer = "Vive" (default) refers to the HTC Vive pro series headsets.
65 # headset_manufacturer = "Oculus" refers to the Quest 1, 2 or 3 headsets (in keeping
66 # with OpenXR terminology
67 
68 
69 sound_end_of_block = "applaudisement.mp3" # sound indicating end of block
70 sound_wrong_response = "buzzer.ogg"
71 handcontroller_id = my_world.handControllerRight.id
72 configurations_set = set()
73 
74 def new_objects(random_value):
75  if(random_value == 1):
76  value = random.randint (0, 1)
77  if(value == 1):
78  color_object = color_base
79  else:
80  color_object = color_distractor
81 
82  my_object = Sphere(size_in_meters= array([0.2, 0.2, 0.2]), color = color_object)
83  else:
84  my_object = Cube(size_in_meters= array([0.2, 0.2, 0.2]), color = color_base)
85  return my_object
86 
88  reticle_pointing_at_object = PointedAt (target_id = my_object.id,
89  activation_cone_origin_id=handcontroller_id,
90  activation_cone_radius_deg=1, activation_cone_depth=500, mode="press",
91  event_name="object_is_pointed_at")
92  reticle_NOT_pointing_at_object = PointedAt (target_id = my_object.id,
93  activation_cone_origin_id=handcontroller_id,
94  activation_cone_radius_deg=1, activation_cone_depth=500, mode="release",
95  event_name="object_is_not_pointed_at")
96  return reticle_pointing_at_object, reticle_NOT_pointing_at_object
97 
99  callback_outline_on = ChangeObjectOutline(object_id=my_object.id, outline_color=RGBColor(r=1.0,g= 1.0, b=1.0,a= 1),
100  effect="activate")
101  callback_outline_off = ChangeObjectOutline(object_id=my_object.id, outline_color=RGBColor(r=1.0,g= 1.0, b=1.0,a= 1),
102  effect="deactivate")
103  return callback_outline_on,callback_outline_off
104 
105 def main():
106  print("Creating an experiment to study visual search in various 3D regions...")
107 
108  my_initial_scene = CheckScene ( my_world = my_world,
109  are_both_hand_controllers_visible= True,
110  text_information = "In this experiment you have "\
111  "to search for the red cube \n among over objects " \
112  "by pointing at it and pressing the trigger.\n" \
113  "When you are ready to start, please press the trigger.")
114  my_world.add_scene (my_initial_scene)
115 
116  # Create the visual scene that will contain the distractors and the target
117  my_scene = VisualScene (is_right_hand_controller_visible = True)
118 
119  # Create a pointer
120  my_laser_beam = PointingLaser ( hand_laser = hand,
121  laser_color = my_laser_color,
122  laser_width = 0.01 )
123  my_scene.place_pointing_laser ( my_laser_beam )
124  my_scene.AddPointingCursor(my_laser_beam)
125 
126 
127  my_world.translate_coordinate_system_along_current(translation = viewpoint_position)
128 
129  audio_wrong = AudioSource(audio_file=sound_wrong_response, is_playing_directly=False)
130  my_scene.place(audio_wrong,my_world)
131  while len(configurations_set) < number_of_distractor+1:
132 
133  radialdistance = random.uniform(radial_distance_min_max_values [0],
134  radial_distance_min_max_values [1])
135  eccentricity = random.gauss(90, 40)
136  halfmeridian = random.randint(0, 360)
137  configuration = (radialdistance, eccentricity, halfmeridian)
138  if tuple(configuration) not in configurations_set:
139  configurations_set.add(tuple(configuration))
140 
141  configurations = [list(config) for config in configurations_set]
142  target_configuration = random.choice(configurations)
143  print("my target is at np.array([radial_distance,eccentricity,half_meridian)) : " +
144  str(target_configuration))
145  for config in configurations:
146  radialdistance = config[0]
147  eccentricity = config[1]
148  halfmeridian = config[2]
149  if(config != target_configuration):
150  my_object = new_objects(random.randint(0, 1)) # Sphere or Cube
151  else:
152  my_object = Cube(size_in_meters= array([0.2, 0.2, 0.2]), color=color_target)
153  my_object.set_perimetric_coordinates(radialDistance=radialdistance,
154  eccentricity=eccentricity, halfMeridian=halfmeridian)
155  my_scene.place(my_object, my_world)
156  inside,outside = create_events_when_pointing_at_objects (my_object)
157  outline_on,outline_off = create_callbacks_to_change_objects(my_object)
158  launchwrong_song = ChangeAudioSettings(audio_id=audio_wrong.id)
159  press = HandController(valid_responses=['right_trigger'],mode="press",event_name="press")
160  if(config != target_configuration):
161  add = AddInteractionPassedToCallback(events=[press], callbacks=[launchwrong_song],
162  effect="activate")
163  remove = AddInteractionPassedToCallback(events=[press], callbacks=[launchwrong_song],
164  effect="deactivate")
165  my_scene.AddInteraction(events=[inside], callbacks=[add,outline_on])
166  my_scene.AddInteraction(events=[outside], callbacks=[remove,outline_off])
167 
168  else:
169  finish = EndCurrentScene(callback_name = "target has been found")
170 
171  add = AddInteractionPassedToCallback(events=[press], callbacks=[finish], effect="activate")
172  remove = AddInteractionPassedToCallback(events=[press], callbacks=[finish], effect="deactivate")
173  # Register Events and Callbacks
174  my_scene.AddInteraction(events=[inside], callbacks=[add,outline_on])
175  my_scene.AddInteraction(events=[outside], callbacks=[remove,outline_off])
176 
177 
178  my_world.add_scene(my_scene)
179 
180  my_world.reset_coordinate_system()
181 
182  calibration = CheckScene(my_world = my_world,text_information= "Congratulations!",are_both_hand_controllers_visible = True)
183  audio = AudioSource(audio_file=sound_end_of_block)
184  calibration.place(audio,my_world)
185  my_world.add_scene(calibration)
186 
187  my_world.write() # create json file containing the parameters of the present experiment
188  print("The .json file corresponding to this experiment has been created.")
189 
190 
191 if __name__ == "__main__":
192  main()
193  LaunchThe3DWorld(username) # Launch the3DWorld with PTVR.
def LaunchThe3DWorld(jsonFileCategory="Externals")
Definition: SystemUtils.py:182
def new_objects(random_value)
def create_events_when_pointing_at_objects(my_object)
def create_callbacks_to_change_objects(my_object)